/* http://shootout.alioth.debian.org/u32q/benchmark.php?test=chameneosredux&lang=gcc&id=2 */

/* The Computer Language Benchmarks Game
   http://shootout.alioth.debian.org/

   contributed by Michael Barker
   based on a Java contribution by Luzius Meisser

   convert to C by dualamd
*/

/* adapted from C sample, using currently available features in the language */

#define blue		0
#define red		1
#define yellow		2
#define Invalid		3

#define Colour		auto
#define BOOL		auto

auto n = 600;

struct Creature;
struct MeetingPlace {
    auto		mut;
    int32_t		meetingsLeft;
    Creature		firstCreature;
};

struct Creature {
   auto			ht;

    MeetingPlace	place;
    int32_t		count;
    int32_t		sameCount;

    Colour		colour;
    int32_t		id;

    BOOL		two_met;
    BOOL		sameid;
};

string_t ColourName[] = {
    "blue", "red", "yellow"
};

#define TRUE		1
#define FALSE		0

auto CreatureID = 0;

Colour doCompliment(Colour c1, Colour c2) {
    switch (c1) {
	case blue:
	    switch (c2) {
		case blue:
		    return blue;
		case red:
		    return yellow;
		case yellow:
		    return red;
		default:
		    goto errlb;
	    }
	case red:
	    switch (c2) {
		case blue:
		    return yellow;
		case red:
		    return red;
		case yellow:
		    return blue;
		default:
		    goto errlb;
	    }
	case yellow:
	    switch (c2) {
		case blue:
		    return red;
		case red:
		    return blue;
		case yellow:
		    return yellow;
		default:
		    goto errlb;
	    }
	default:
	    break;
    }

errlb:
    print("Invalid colour\n");
    throw 1;
}

string_t NUMBERS[] = {
    "zero", "one", "two", "three", "four", "five",
    "six", "seven", "eight", "nine"
};

/* convert integer to number string: 1234 -> "one two three four" */
string_t formatNumber(auto n, string_t outbuf) {
    auto	i, ichar;
    string_t	tmp1 = new uint8_t[64];
    string_t	tmp2 = new uint8_t[64];

    ichar = printf(tmp1, "%d", n);

    printf(tmp2, "");
    for (i = 0; i < ichar; i++) {
	printf(outbuf, "%s %s", tmp2, NUMBERS[tmp1[i] - '0']);
	printf(tmp2, "%s", outbuf);
    }

   return outbuf;
}

MeetingPlace MeetingPlace_Init(int32_t meetings) {
    MeetingPlace m = new MeetingPlace;

    m.mut = mutex();
    m.meetingsLeft = meetings;
    m.firstCreature = 0;

    return m;
}

BOOL Meet(Creature cr) {
    BOOL		retval = TRUE;
    MeetingPlace	mp = cr.place;

    lock(mp.mut);

    if (mp.meetingsLeft > 0) {
	if (mp.firstCreature == 0) {
	    cr.two_met = FALSE;
	    mp.firstCreature = cr;
	}
	else {
	    Creature first;
	    Colour newColour;

	    first = mp.firstCreature;
	    newColour = doCompliment(cr.colour, first.colour);

	    cr.sameid = cr.id == first.id;
	    cr.colour = newColour;
	    cr.two_met = TRUE;

	    first.sameid = cr.sameid;
	    first.colour = newColour;
	    first.two_met = TRUE;

	    mp.firstCreature = 0;
	    --mp.meetingsLeft;
	}
    }
    else
	retval = FALSE;

    unlock(mp.mut);
    return retval;
}

void CreatureThreadRun(Creature cr) {
    while (TRUE) {
	if (Meet(cr)) {
	    while (cr.two_met == FALSE)
		;	/* removal of yield affects here... */

	    if (cr.sameid)
		++cr.sameCount;
	    ++cr.count;
	}
	else
	    break;
   }
}

Creature Creature_Init(MeetingPlace place, Colour colour) {
    Creature cr = new Creature;

    cr.place = place;
    cr.count = cr.sameCount = 0;

    cr.id = ++CreatureID;
    cr.colour = colour;
    cr.two_met = FALSE;

    cr.ht = thread(&CreatureThreadRun, cr);

    return cr;
}

/* format meeting times of each creature to string */
string_t Creature_getResult(Creature cr, string_t str) {
    string_t numstr = new uint8_t[256];
    formatNumber(cr.sameCount, numstr);

    printf(str, "%d%s", cr.count, numstr);
    return str;
}

void runGame(int32_t n_meeting, int32_t ncolor, Colour colours[]) {
    auto		i;
    auto		total = 0;
    string_t		str = new uint8_t[256];
    MeetingPlace	place;
    Creature		creatures[] = new Creature[ncolor];

    place = MeetingPlace_Init(n_meeting);

    /* print initial color of each creature */
    for (i = 0; i < ncolor; i++) {
	print("%s ", ColourName[colours[i]]);
	creatures[i] = Creature_Init(place, colours[i]);
    }
    print("\n");

    /* wait for them to meet */
    for (i = 0; i < ncolor; i++)
	join(creatures[i].ht);

    /* print meeting times of each creature */
    for (i = 0; i < ncolor; i++) {
	print("%s\n", Creature_getResult(creatures[i], str));
	total += creatures[i].count;
    }

    /* print total meeting times, should equal n_meeting */
    print("%s\n\n", formatNumber(total, str));
}

void printColours(Colour c1, Colour c2) {
    print("%s + %s -> %s\n",
	  ColourName[c1],
	  ColourName[c2],
	  ColourName[doCompliment(c1, c2)]);
}

void printColoursTable(void) {
    printColours(blue, blue);
    printColours(blue, red);
    printColours(blue, yellow);
    printColours(red, blue);
    printColours(red, red);
    printColours(red, yellow);
    printColours(yellow, blue);
    printColours(yellow, red);
    printColours(yellow, yellow);
}

try {
    printColoursTable();
    print("\n");

    Colour r1[] = {
	blue, red, yellow
    };
    Colour r2[] = {
	blue, red, yellow,
	red, yellow, blue,
	red, yellow, red, blue
    };

   runGame(n, sizeof(r1), r1);
   runGame(n, sizeof(r2), r2);
}
catch (e) {
    // emulate error & exit()
    print("exception %d caugth\n", e);
    throw;
}
